package wang.wangby.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 主键生成器 1-10机器编号,11-22同一毫秒可产生的序列号,23-63当前毫秒值
 */
public class IdWorker{
	private Logger log=LoggerFactory.getLogger(this.getClass());

	public static final int machineBits=10;// 机器位数
	public static final int seqBits=12;// 序列号位数
	public static final int timeShift=machineBits+seqBits;// 时间往左移动位数
	public static final long maxSeq=-1L^(-1L<<seqBits);// 最大允许序列号(4095)
	public static final long maxMachine=-1L^(-1L<<machineBits);// 最大允许机器号(1023)
	//为了防止超过long的最大值,时间戳生成后减去这个值
	private static final long begin=new DateTime("2016-01-01").getTime();
	private int machineCode;
	private long lastTime;
	private long sequence=1;// 因为0无法向左做位移操作,序列号从1开始

	/**
	 * @param machineCode 机器编号
	 * 
	 */
	public IdWorker(int machineCode){
		this.machineCode=machineCode;
		if(machineCode>maxMachine){
			throw new RuntimeException("机器编号不能大于"+maxMachine);
		}
		if((currentTimeMillis()-begin)<<timeShift<0){
			long max=(Long.MAX_VALUE>>timeShift)+begin;//2085-09-06
			throw new RuntimeException("当前时间超过"+new DateTime(max)+",生成器已无法使用");
		}
		log.info("主键生成器创建完毕,当前机器号:"+machineCode);
	}

	public synchronized long nextId(){
		long time=currentTimeMillis()-begin;
		if(time!=lastTime){
			lastTime=time;
			sequence=1;
		}else{
			sequence++;
		}
		if(sequence>maxSeq){
			throw new RuntimeException("当前不支持同一毫秒内生成主键超过:"+maxSeq);
		}

		return (time<<timeShift)|(sequence<<machineBits)|machineCode;
	}

	//获取当前时间单独弄一个方法,方便测试
	public long currentTimeMillis(){
		return System.currentTimeMillis();
	}
}